using System;
using System.Collections ;
using EnvDTE;


namespace AopDotNet
{
		namespace Editor
		{
			/// <summary>
			/// logs all the editing that takes place in the files
			/// </summary>
			public class Logger
			{
				private Hashtable LogEntries;

				public Logger()
				{
					LogEntries=new Hashtable(20);
				}
				public  ArrayList GetFileChanges(string fileName)
				{
					if (LogEntries.Contains (fileName)==false)
						return null;
					return (ArrayList)LogEntries[fileName];

				}
				public void AddLogEntry (string fileName,LogEntry entry)
				{
					ArrayList entries;
					if (LogEntries.Contains (fileName)==false)
					{
						entries=new ArrayList(30);
					}
					else
					{
						entries=(ArrayList)LogEntries[fileName];
					}
			
					entries.Add (entry);
			
					LogEntries[fileName]=entries;
				}
			}

	
			/// <summary>
			/// the information of each edit operation that is to be logged
			/// </summary>
			public class LogEntry
			{
				public  LogEntry(int si,int lines,int vLen,int hLen)
				{
					startIndexOfChages=si;
					hLengthOfChanges=hLen;
					vLengthOfChanges=vLen;
					startLineOfChanges=lines;
				}
				/// <summary>
				///	where the editing starts 
				/// </summary>
				public int startIndexOfChages;

				/// <summary>
				/// the number of lines added or deleted
				/// </summary>
				public int vLengthOfChanges;

				/// <summary>
				/// the number of characters added or removed in the line
				/// </summary>
				public int hLengthOfChanges;

				/// <summary>
				/// the line where the edit starts
				/// </summary>
				public int startLineOfChanges;
			}


			/// <summary>
			/// handles the editing process
			/// </summary>	
			public class EditingMonitor
			{
				private Language curLang;//1 csharp 2 vb 3 jsharp
				private Logger logger;
				private bool isLogging;
				private string fileName;
				private int startOfChangeIndex;
				private int startOfChangeLine;
				private int updatedStartIndex;  //the actual index (that is after adding the replacement) where inserion occurs
				private int updatedStartLine;   // the actual line (that is after adding the replacement) where inserion occurs

				private int nlastAddedLines;
			
				public EditingMonitor(Language lang)
				{
					logger=new Logger ();
					isLogging=false;
					startOfChangeIndex=-1;
					updatedStartIndex=-1;
					startOfChangeLine=-1;
					updatedStartLine=-1;
					nlastAddedLines=-1;
					curLang =lang;
				}

				/// <summary>
				/// adjust the edit point to be aware of the past change and start logging
				/// </summary>
				/// <param name="ep"></param>
			
				private void StartLoga(EditPoint ep)
				{

					isLogging=true;
					fileName=ep.Parent.Parent.ProjectItem.get_FileNames (0);
								
					//stChangeIndex=ep.AbsoluteCharOffset ;
					/*int*/ startOfChangeIndex=ep.LineCharOffset  ;
					/*int*/ startOfChangeLine=ep.Line ;
				
					ArrayList changes=logger.GetFileChanges (fileName);
					if (changes!=null)
					{
						AccumalateChanges(ref ep,changes);

						//int lineReplacement=0;
						//int offsetReplacement=0;
						//for (int i=0;i<=changes.Count -1;i++)
						//{
						//	LogEntry entry=(LogEntry)changes[i];
						//	if (entry.startLineOfChanges <=/*updatedStartLine*/startOfChangeLine)
						//	{
						//		lineReplacement+=entry.vLengthOfChanges  ;
						//		if ((entry.startLineOfChanges ==startOfChangeLine) && (entry.startIndexOfChages <= startOfChangeIndex ))
						//		{
						//			offsetReplacement+=entry.hLengthOfChanges  ;
						//		}
						//	}
						
						//}
						//int newOffset=ep.LineCharOffset+offsetReplacement;
						//int newLine=ep.Line+lineReplacement;
						//ChangeLoc(ep,newLine,newOffset);
						if (nlastAddedLines!=-1)
						{
							EditPoint endDoc=ep.Parent.EndPoint .CreateEditPoint ();
							endDoc.LineUp (nlastAddedLines-1);
						
							endDoc.Delete  (ep.Parent .EndPoint );
						}
					}
					updatedStartIndex=ep.LineCharOffset  ;
					updatedStartLine=ep.Line ;

				}
				public void UpdateEditPoint(ref EditPoint ep)
				{
					
					if (curLang==Language.VB || curLang==Language.CSharp || curLang==Language.JSharp)
						return;
					string fileName=ep.Parent.Parent.ProjectItem.get_FileNames (0);
									
					startOfChangeIndex=ep.LineCharOffset  ;
					startOfChangeLine=ep.Line ;
				
					ArrayList changes=logger.GetFileChanges (fileName);
					if (changes!=null)
					{
						AccumalateChanges(ref ep,changes);
					}
			
				}
				public void  StartLog(ref EditPoint ep,bool IsUpdated)
				{
					
					if (curLang==Language.VB|| curLang==Language.CSharp || curLang==Language.JSharp )
						return;
					fileName=ep.Parent.Parent.ProjectItem.get_FileNames (0);
					isLogging=true;
					if (IsUpdated )
					{
						updatedStartIndex=ep.LineCharOffset  ;
						updatedStartLine=ep.Line ;
					}
					else
					{
				
									
						startOfChangeIndex=ep.LineCharOffset  ;
						startOfChangeLine=ep.Line ;
				
						ArrayList changes=logger.GetFileChanges (fileName);
						if (changes!=null)
						{
							AccumalateChanges(ref ep,changes);

							if (nlastAddedLines!=-1)
							{
								EditPoint endDoc=ep.Parent.EndPoint .CreateEditPoint ();
								endDoc.LineUp (nlastAddedLines-1);
						
								endDoc.Delete  (ep.Parent .EndPoint );
							}
						}
						updatedStartIndex=ep.LineCharOffset  ;
						updatedStartLine=ep.Line ;
					}
				}
				private void ChangeLoc(ref EditPoint ep,int newLine,int newOffset)
				{
					try
					{
						ep.MoveToLineAndOffset (  newLine, newOffset);
					}
					catch(Exception exp)
					{
						ep.MoveToLineAndOffset (newLine,1);
						string lineText=ep.GetText (newOffset);
						int endOfLineIndex=lineText.IndexOf ("\n");
						if (endOfLineIndex!=-1)
						{
							int dif = newOffset-endOfLineIndex;
							ep.MoveToLineAndOffset (newLine,endOfLineIndex+1);
							string s=new string(' ',dif);
							ep.Insert (s);
						
						}
					}
				}

				private void AccumalateChanges(ref EditPoint ep,ArrayList changes)
				{
					int lineReplacement=0;
					int offsetReplacement=0;
					for (int i=0;i<=changes.Count -1;i++)
					{
						LogEntry entry=(LogEntry)changes[i];
						if (entry.startLineOfChanges <=/*updatedStartLine*/startOfChangeLine)
						{
							lineReplacement+=entry.vLengthOfChanges  ;
							if ((entry.startLineOfChanges ==startOfChangeLine) && (entry.startIndexOfChages <= startOfChangeIndex ))
							{
								offsetReplacement+=entry.hLengthOfChanges  ;
							}
						}
						
					}
					int newOffset=ep.LineCharOffset+offsetReplacement;
					int newLine=ep.Line+lineReplacement;
					ChangeLoc(ref ep,newLine,newOffset);
				}
				/// <summary>
				/// logs the changes that happended
				/// </summary>
				/// <param name="ep"></param>
				public void EndLog(ref EditPoint ep)
				{
					
					if (curLang==Language.VB ||curLang==Language.CSharp ||curLang==Language.JSharp )
						return;
					isLogging=false;

					//calculate the replcaement 
					int lineReplacement=ep.Line-updatedStartLine;//eshta if the changes is additive

					nlastAddedLines=lineReplacement;
					int offsetReplacement=ep.LineCharOffset-updatedStartIndex;//eshta if the changes is additive
					//but what if it's not
					LogEntry le=new LogEntry (startOfChangeIndex,startOfChangeLine,lineReplacement,offsetReplacement);
					logger.AddLogEntry (fileName,le);
				
					//expand the document so that we can move to points that are past the end of the unupdated doc
				

					EditPoint endDoc=ep.Parent.EndPoint .CreateEditPoint ();
					string s =new string ('\n',lineReplacement);
					endDoc.Insert (s);

					fileName="";
					//fileName=e.ProjectItem.get_FileNames (0);
					startOfChangeIndex=-1;
					startOfChangeLine=-1;
					updatedStartIndex=-1;
					updatedStartLine=-1;
				}

				/*		public EditPoint GetStartEditPoint(CodeElement el)
						{
							EditPoint ep=el.StartPoint.CreateEditPoint ();
							fileName=ep.Parent.Parent.ProjectItem.get_FileNames (0);
							startOfChangeIndex=ep.LineCharOffset  ;
							startOfChangeLine=ep.Line ;
				
							ArrayList changes=logger.GetFileChanges (fileName);
							if (changes!=null)
							{
								AccumalateChanges(ep,changes);
							}
							return ep;
						}
						public EditPoint GetStartEditPoint(CodeElement el,EnvDTE.vsCMPart portion)
						{
							EditPoint ep=el.GetStartPoint(portion).CreateEditPoint ();
							fileName=ep.Parent.Parent.ProjectItem.get_FileNames (0);
							startOfChangeIndex=ep.LineCharOffset  ;
							startOfChangeLine=ep.Line ;
				
							ArrayList changes=logger.GetFileChanges (fileName);
							if (changes!=null)
							{
								AccumalateChanges(ep,changes);
							}
							return ep;
						}
						public EditPoint GetEndEditPoint(CodeElement el)
						{
							EditPoint ep=el.EndPoint.CreateEditPoint ();
							fileName=ep.Parent.Parent.ProjectItem.get_FileNames (0);
							startOfChangeIndex=ep.LineCharOffset  ;
							startOfChangeLine=ep.Line ;
				
							ArrayList changes=logger.GetFileChanges (fileName);
							if (changes!=null)
							{
								AccumalateChanges(ep,changes);
							}
							return ep;

						}
						public EditPoint GetEndEditPoint(CodeElement el,EnvDTE.vsCMPart portion)
						{
							EditPoint ep=el.GetStartPoint(portion).CreateEditPoint ();
							fileName=ep.Parent.Parent.ProjectItem.get_FileNames (0);
							startOfChangeIndex=ep.LineCharOffset  ;
							startOfChangeLine=ep.Line ;
				
							ArrayList changes=logger.GetFileChanges (fileName);
							if (changes!=null)
							{
								AccumalateChanges(ep,changes);
							}
							return ep;
						}*/
			}	
		}
	
}